import functools


def my_func(a, b=2):
    """Docstring for my_func"""
    print('  called my_func with: ', (a, b))


def show_details(name, f, is_partial=False):
    """show details of a callable object"""
    print('{}: '.format(name))
    print('  object: ', f)
    if not is_partial:
        print('__name__: ', f.__name__)
    else:
        print('  func: ', f.func)
        print('  args: ', f.args)
        print('  keywords: ', f.keywords)
    return


show_details('my_func', my_func)
my_func('a', 3)
print()

# set a different default value for 'b', but require the caller to provide 'a'.
p1 = functools.partial(my_func, b=4)
show_details('partial with named default', p1, True)
p1('passing a')
p1('override b', b=5)
print()

# set a different default value for both 'a' and 'b'.
p2 = functools.partial(my_func, 'default a', b=99)
show_details('partial with defaults', p2, True)
p2()
p2(b='override b')
print()

# print('insufficient arguments:')
# p1()

print(dir(my_func))
print(my_func.__doc__)
print(dir(p1))
print(dir(p2))
print(p1.__doc__)
